﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices.AccountManagement;
using System.DirectoryServices;
using ActiveDs;
using System.Data;
using System.Configuration;

public class ADFuncs
{
    #region Variables

    private string sDomain = ConfigurationManager.AppSettings["ADDomain"];
    private string sDefaultOU = ConfigurationManager.AppSettings["ADDefaultOU"];
    private string sDefaultRootOU = ConfigurationManager.AppSettings["ADDefaultRootOU"];
    private string sServiceUser = ConfigurationManager.AppSettings["ADUser"];
    private string sServicePassword = ConfigurationManager.AppSettings["ADPassword"];
    private string sSearchOUs = ConfigurationManager.AppSettings["ADPasswordReminderOUs"];

    #endregion

    #region Validate Methods

    /// <summary>
    /// Validates the username and password of a given user
    /// </summary>
    /// <param name="sUserName">The username to validate</param>
    /// <param name="sPassword">The password of the username to validate</param>
    /// <returns>Returns True of user is valid</returns>
    public bool ValidateCredentials(string sUserName, string sPassword)
    {
        PrincipalContext oPrincipalContext = GetPrincipalContext();
        return oPrincipalContext.ValidateCredentials(sUserName, sPassword);
    }

    /// <summary>
    /// Checks if the User Account is Expired
    /// </summary>
    /// <param name="sUserName">The username to check</param>
    /// <returns>Returns true if Expired</returns>
    public bool IsUserExpired(string sUserName)
    {
        UserPrincipal oUserPrincipal = GetUser(sUserName);
        if (oUserPrincipal.AccountExpirationDate != null)
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    /// <summary>
    /// Checks if the User's Account password is expired
    /// </summary>
    /// <param name="sUserName">The username to check</param>
    /// <returns>Returns true if Expired</returns>
    public bool IsUserPasswordExpired(string sUserName)
    {
        UserPrincipal oUserPrincipal = GetUser(sUserName);
        DirectoryEntry entry = new DirectoryEntry("LDAP://" + oUserPrincipal.DistinguishedName);
        IADsUser native = (IADsUser)entry.NativeObject;
        // compareto returns zero if equal or less than zero if smaller.
        if (native.PasswordExpirationDate.CompareTo(DateTime.Today) <= 0 && !oUserPrincipal.PasswordNeverExpires)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    /// <summary>
    /// Finds the date that the specified user's password will expire
    /// </summary>
    /// <param name="sUserName">The username to check</param>
    /// <returns>Returns the date the password will expire.  
    ///          If the password never expires, returns a date 1000 years in the future.</returns>
    public DateTime GetUserPasswordExpiration(string sUserName)
    {
        UserPrincipal oUserPrincipal = GetUser(sUserName);
        DirectoryEntry entry = new DirectoryEntry("LDAP://" + oUserPrincipal.DistinguishedName);
        IADsUser native = (IADsUser)entry.NativeObject;
        if (oUserPrincipal.PasswordNeverExpires)
        {
            // Password never expires
            return DateTime.Today.AddYears(1000);
        }
        else
        {
            // Password has an expiration date
            return native.PasswordExpirationDate;
        }
    }

    /// <summary>
    /// Checks if user exists on AD
    /// </summary>
    /// <param name="sUserName">The username to check</param>
    /// <returns>Returns true if username Exists</returns>
    public bool IsUserExisiting(string sUserName)
    {
        if (GetUser(sUserName) == null)
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    /// <summary>
    /// Checks if user account is locked
    /// </summary>
    /// <param name="sUserName">The username to check</param>
    /// <returns>Returns true of Account is locked</returns>
    public bool IsAccountLocked(string sUserName)
    {
        UserPrincipal oUserPrincipal = GetUser(sUserName);
        return oUserPrincipal.IsAccountLockedOut();
    }
    #endregion

    #region Search Methods

    /// <summary>
    /// Gets a certain user on Active Directory
    /// </summary>
    /// <param name="sUserName">The username to get</param>
    /// <returns>Returns the UserPrincipal Object</returns>
    public UserPrincipal GetUser(string sUserName)
    {
        PrincipalContext oPrincipalContext = GetPrincipalContext();

       UserPrincipal oUserPrincipal =
           UserPrincipal.FindByIdentity(oPrincipalContext, sUserName);
        return oUserPrincipal;
    }

    /// <summary>
    /// Gets a certain group on Active Directory
    /// </summary>
    /// <param name="sGroupName">The group to get</param>
    /// <returns>Returns the GroupPrincipal Object</returns>
    public GroupPrincipal GetGroup(string sGroupName)
    {
        PrincipalContext oPrincipalContext = GetPrincipalContext();

        GroupPrincipal oGroupPrincipal =
           GroupPrincipal.FindByIdentity(oPrincipalContext, sGroupName);
        return oGroupPrincipal;
    }

    /// <summary>
    /// Gets a list of user accounts whose passwords will expire in the next numDays and how long until they expire.
    /// </summary>
    /// <param name="numDays"></param>
    /// <returns></returns>
    public DataTable GetUsersWithPasswordsExpiring(int numDays)
    {
        DataTable ResultTable = new DataTable("UserTable");
        ResultTable.Columns.Add("Username");
        ResultTable.Columns.Add("DateExpires");
        List<PrincipalContext> ContextList = new List<PrincipalContext>();

        if(sSearchOUs == "")
        {
            ContextList.Add(GetPrincipalContext());
        }
        else
        {
            foreach(string ouName in sSearchOUs.Split(','))
            {
                ContextList.Add(GetPrincipalContext("OU="+ouName+","+sDefaultRootOU));
            }
        }

        foreach(PrincipalContext context in ContextList)
        {
            using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
            {
                foreach (var result in searcher.FindAll())
                {
                    DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
                    IADsUser native = (IADsUser)de.NativeObject;
                    if (native.PasswordExpirationDate.Subtract(DateTime.Today).Days <= numDays 
                        && !((UserPrincipal)result).PasswordNeverExpires
                        && !native.AccountDisabled)
                    {
                        DataRow TempRow = ResultTable.NewRow(); 
                        TempRow["Username"] = de.Properties["samAccountName"].Value;
                        TempRow["DateExpires"] = native.PasswordExpirationDate;
                        ResultTable.Rows.Add(TempRow);
                    }
                }
            }
        }

        return ResultTable;

    }

    #endregion

    #region User Account Methods

    /// <summary>
    /// Sets the user password
    /// </summary>
    /// <param name="sUserName">The username to set</param>
    /// <param name="sNewPassword">The new password to use</param>
    /// <param name="sMessage">Any output messages</param>
    public void SetUserPassword(string sUserName, string sNewPassword, out string sMessage)
    {
        try
        {
            UserPrincipal oUserPrincipal = GetUser(sUserName);
            oUserPrincipal.SetPassword(sNewPassword);
            sMessage = "";
        }
        catch (Exception ex)
        {
            if (ex.Message.Contains("does not meet the password policy requirements"))
            {
                sMessage = "The password does not meet one of the following: " + System.Environment.NewLine +
                    "Complexity Requirements (one lowercase, one uppercase, and one number or special character), " +
                    System.Environment.NewLine + "History Requirements (cannot be one of the last 5 passwords), " +
                    System.Environment.NewLine + "or Length Requirements (at least 7 characters long).  " +
                    System.Environment.NewLine + "Please try again.";
            }
            else
            {
                sMessage = ex.Message;
            }
        }
    }

    /// <summary>
    /// Enables a disabled user account
    /// </summary>
    /// <param name="sUserName">The username to enable</param>
    public void EnableUserAccount(string sUserName)
    {
        UserPrincipal oUserPrincipal = GetUser(sUserName);
        oUserPrincipal.Enabled = true;
        oUserPrincipal.Save();
    }

    /// <summary>
    /// Force disabling of a user account
    /// </summary>
    /// <param name="sUserName">The username to disable</param>
    public void DisableUserAccount(string sUserName)
    {
        UserPrincipal oUserPrincipal = GetUser(sUserName);
        oUserPrincipal.Enabled = false;
        oUserPrincipal.Save();
    }

    /// <summary>
    /// Force expire password of a user
    /// </summary>
    /// <param name="sUserName">The username to expire the password</param>
    public void ExpireUserPassword(string sUserName)
    {
        UserPrincipal oUserPrincipal = GetUser(sUserName);
        oUserPrincipal.ExpirePasswordNow();
        oUserPrincipal.Save();
    }

    /// <summary>
    /// Unlocks a locked user account
    /// </summary>
    /// <param name="sUserName">The username to unlock</param>
    public void UnlockUserAccount(string sUserName)
    {
        UserPrincipal oUserPrincipal = GetUser(sUserName);
        oUserPrincipal.UnlockAccount();
        oUserPrincipal.Save();
    }

    /// <summary>
    /// Creates a new user on Active Directory
    /// </summary>
    /// <param name="sOU">The OU location you want to save your user</param>
    /// <param name="sUserName">The username of the new user</param>
    /// <param name="sPassword">The password of the new user</param>
    /// <param name="sGivenName">The given name of the new user</param>
    /// <param name="sSurname">The surname of the new user</param>
    /// <returns>returns the UserPrincipal object</returns>
    public UserPrincipal CreateNewUser(string sOU,
       string sUserName, string sPassword, string sGivenName, string sSurname)
    {
        if (!IsUserExisiting(sUserName))
        {
            PrincipalContext oPrincipalContext = GetPrincipalContext(sOU);

            UserPrincipal oUserPrincipal = new UserPrincipal
               (oPrincipalContext, sUserName, sPassword, true /*Enabled or not*/);

            //User Log on Name
            oUserPrincipal.UserPrincipalName = sUserName;
            oUserPrincipal.GivenName = sGivenName;
            oUserPrincipal.Surname = sSurname;
            oUserPrincipal.Save();

            return oUserPrincipal;
        }
        else
        {
            return GetUser(sUserName);
        }
    }

    /// <summary>
    /// Deletes a user in Active Directory
    /// </summary>
    /// <param name="sUserName">The username you want to delete</param>
    /// <returns>Returns true if successfully deleted</returns>
    public bool DeleteUser(string sUserName)
    {
        try
        {
            UserPrincipal oUserPrincipal = GetUser(sUserName);

            oUserPrincipal.Delete();
            return true;
        }
        catch
        {
            return false;
        }
    }

    #endregion

    #region Group Methods

    /// <summary>
    /// Creates a new group in Active Directory
    /// </summary>
    /// <param name="sOU">The OU location you want to save your new Group</param>
    /// <param name="sGroupName">The name of the new group</param>
    /// <param name="sDescription">The description of the new group</param>
    /// <param name="oGroupScope">The scope of the new group</param>
    /// <param name="bSecurityGroup">True is you want this group 
    /// to be a security group, false if you want this as a distribution group</param>
    /// <returns>Returns the GroupPrincipal object</returns>
    public GroupPrincipal CreateNewGroup(string sOU, string sGroupName,
       string sDescription, GroupScope oGroupScope, bool bSecurityGroup)
    {
        PrincipalContext oPrincipalContext = GetPrincipalContext(sOU);

        GroupPrincipal oGroupPrincipal = new GroupPrincipal(oPrincipalContext, sGroupName);
        oGroupPrincipal.Description = sDescription;
        oGroupPrincipal.GroupScope = oGroupScope;
        oGroupPrincipal.IsSecurityGroup = bSecurityGroup;
        oGroupPrincipal.Save();

        return oGroupPrincipal;
    }

    /// <summary>
    /// Adds the user for a given group
    /// </summary>
    /// <param name="sUserName">The user you want to add to a group</param>
    /// <param name="sGroupName">The group you want the user to be added in</param>
    /// <returns>Returns true if successful</returns>
    public bool AddUserToGroup(string sUserName, string sGroupName)
    {
        try
        {
            UserPrincipal oUserPrincipal = GetUser(sUserName);
            GroupPrincipal oGroupPrincipal = GetGroup(sGroupName);
            if (oUserPrincipal == null || oGroupPrincipal == null)
            {
                if (!IsUserGroupMember(sUserName, sGroupName))
                {
                    oGroupPrincipal.Members.Add(oUserPrincipal);
                    oGroupPrincipal.Save();
                }
            }
            return true;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Removes user from a given group
    /// </summary>
    /// <param name="sUserName">The user you want to remove from a group</param>
    /// <param name="sGroupName">The group you want the user to be removed from</param>
    /// <returns>Returns true if successful</returns>
    public bool RemoveUserFromGroup(string sUserName, string sGroupName)
    {
        try
        {
            UserPrincipal oUserPrincipal = GetUser(sUserName);
            GroupPrincipal oGroupPrincipal = GetGroup(sGroupName);
            if (oUserPrincipal == null || oGroupPrincipal == null)
            {
                if (IsUserGroupMember(sUserName, sGroupName))
                {
                    oGroupPrincipal.Members.Remove(oUserPrincipal);
                    oGroupPrincipal.Save();
                }
            }
            return true;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// Checks if user is a member of a given group
    /// </summary>
    /// <param name="sUserName">The user you want to validate</param>
    /// <param name="sGroupName">The group you want to check the 
    /// membership of the user</param>
    /// <returns>Returns true if user is a group member</returns>
    public bool IsUserGroupMember(string sUserName, string sGroupName)
    {
        UserPrincipal oUserPrincipal = GetUser(sUserName);
        GroupPrincipal oGroupPrincipal = GetGroup(sGroupName);

        if (oUserPrincipal == null || oGroupPrincipal == null)
        {
            return oGroupPrincipal.Members.Contains(oUserPrincipal);
        }
        else
        {
            return false;
        }
    }

    /// <summary>
    /// Gets a list of the users group memberships
    /// </summary>
    /// <param name="sUserName">The user you want to get the group memberships</param>
    /// <returns>Returns an arraylist of group memberships</returns>
    public ArrayList GetUserGroups(string sUserName)
    {
        ArrayList myItems = new ArrayList();
        UserPrincipal oUserPrincipal = GetUser(sUserName);

        PrincipalSearchResult<Principal> oPrincipalSearchResult = oUserPrincipal.GetGroups();

        foreach (Principal oResult in oPrincipalSearchResult)
        {
            myItems.Add(oResult.Name);
        }
        return myItems;
    }

    /// <summary>
    /// Gets a list of the users authorization groups
    /// </summary>
    /// <param name="sUserName">The user you want to get authorization groups</param>
    /// <returns>Returns an arraylist of group authorization memberships</returns>
    public ArrayList GetUserAuthorizationGroups(string sUserName)
    {
        ArrayList myItems = new ArrayList();
        UserPrincipal oUserPrincipal = GetUser(sUserName);

        PrincipalSearchResult<Principal> oPrincipalSearchResult =
                   oUserPrincipal.GetAuthorizationGroups();

        foreach (Principal oResult in oPrincipalSearchResult)
        {
            myItems.Add(oResult.Name);
        }
        return myItems;
    }

    #endregion

    #region Helper Methods

    /// <summary>
    /// Gets the base principal context
    /// </summary>
    /// <returns>Returns the PrincipalContext object</returns>
    public PrincipalContext GetPrincipalContext()
    {
        // The Simplebind context option seems to break authentiation with a 2008 domain controller.
        //PrincipalContext oPrincipalContext = new PrincipalContext
        //   (ContextType.Domain, sDomain, sDefaultOU, ContextOptions.SimpleBind,
        //   sServiceUser, sServicePassword); 
        PrincipalContext oPrincipalContext = new PrincipalContext
           (ContextType.Domain, sDomain, sDefaultOU, 
           sServiceUser, sServicePassword);
        return oPrincipalContext;
    }

    /// <summary>
    /// Gets the principal context on specified OU
    /// </summary>
    /// <param name="sOU">The OU you want your Principal Context to run on</param>
    /// <returns>Returns the PrincipalContext object</returns>
    public PrincipalContext GetPrincipalContext(string sOU)
    {
        //PrincipalContext oPrincipalContext =
        //   new PrincipalContext(ContextType.Domain, sDomain, sOU,
        //   ContextOptions.SimpleBind, sServiceUser, sServicePassword);
        PrincipalContext oPrincipalContext =
           new PrincipalContext(ContextType.Domain, sDomain, sOU,
           sServiceUser, sServicePassword);
        return oPrincipalContext;
    }

    #endregion
}